package com.shiguiwu.springmybatis.mybatis;

import com.shiguiwu.springmybatis.mybatis.mapper.BookMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * @description: 批量处理
 * @author: stone
 * @date: Created by 2022/5/5 22:17
 * @version: 1.0.0
 * @pakeage: com.shiguiwu.springmybatis.mybatis
 */
@Slf4j
public class MybatisBatchUtils {

    /**
     * 每次处理1000条
     */
    private static final int BATCH_SIZE = 1000;
    private static final int nThread = 8;


    /**
     * 批量处理修改或者插入
     *
     * @param data     需要被处理的数据
     * @param mapperClass  Mybatis的Mapper类
     * @param function 自定义处理逻辑
     * @return int 影响的总行数
     */
    public static  <T,U,R> int batchUpdateOrInsert(List<T> data, Class<U> mapperClass, BiFunction<T,U,R> function) {
        int i = 1;
        SqlSession batchSqlSession = MapperTests.FACTORY.openSession(ExecutorType.BATCH);
        try {
            U mapper = batchSqlSession.getMapper(mapperClass);
            int size = data.size();
            for (T element : data) {
                function.apply(element,mapper);
                if ((i % BATCH_SIZE == 0) || i == size) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            // 非事务环境下强制commit，事务情况下该commit相当于无效
            batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
        } catch (Exception e) {
            batchSqlSession.rollback();
            throw e;
        } finally {
            batchSqlSession.close();
        }
        return i - 1;
    }

    public static void main(String[] args) throws InterruptedException {

        List<Book> books = new ArrayList<>();
        for (int i = 0; i < 30000; i++) {
            Book b = new Book();
            b.setBookName(i + "批量");
            books.add(b);
        }
        ExecutorService service = Executors.newFixedThreadPool(nThread);

        int n = books.size() / nThread;
        long start = System.currentTimeMillis();
        for (int i = 0; i < nThread; i++) {
            int s = i * n ; //0,10,20,30,40,50,60
            List<Book> collect = books.stream().skip(s).limit(n).collect(Collectors.toList());


            service.execute(() -> MybatisBatchUtils.batchUpdateOrInsert(collect, BookMapper.class, (item, o) -> o.insert(item))) ;

        }


        //long start = System.currentTimeMillis();
        //int s = 0;
        //int limit = 1000;
        //MybatisBatchUtils.batchUpdateOrInsert(books, BookMapper.class, (item, o) -> o.insert(item));
        //
        //
        service.shutdown();
        service.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
        log.info("执行的时间为 ===> {}" ,(System.currentTimeMillis() - start)/1000);


        //SqlSession sqlSession = MapperTests.FACTORY.openSession(true);
        //
        //BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        //start = System.currentTimeMillis();
        //books.forEach(mapper::insert);
        //
        //log.info("执行的时间为2 ===> {}" ,(System.currentTimeMillis() - start)/1000);
    }

    static void bach(List<Book> books, int s, int limit) {
        MybatisBatchUtils.batchUpdateOrInsert(books.stream().skip(s).limit(limit).collect(Collectors.toList()), BookMapper.class, (item, o) -> o.insert(item));
        bach(books, s + limit, limit);

    }

}
